/*
 * Copyright (c) 2015. For Intelligent Group.
 */

package com.intelligent.ispc.core.service.impl;

import com.google.common.collect.Lists;
import com.intelligent.ispc.common.mapper.BeanMapper;
import com.intelligent.ispc.common.mapper.BeanUtil;
import com.intelligent.ispc.common.mapper.JsonMapper;
import com.intelligent.ispc.common.persistence.DynamicSpecifications;
import com.intelligent.ispc.common.persistence.SearchFilter;
import com.intelligent.ispc.common.utils.ApplicationException;
import com.intelligent.ispc.common.utils.ExceptionMessages;
import com.intelligent.ispc.core.dto.*;
import com.intelligent.ispc.core.entity.*;
import com.intelligent.ispc.core.repository.jpa.*;
import com.intelligent.ispc.core.service.DataDictionaryService;
import com.intelligent.ispc.core.service.GroupService;
import com.intelligent.ispc.core.service.ProjectService;
import com.intelligent.ispc.core.service.SecurityService;
import com.intelligent.ispc.utils.DDCategory;
import com.intelligent.ispc.utils.DataDictionaryReflectUtil;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;

import javax.persistence.criteria.*;
import java.util.Collections;
import java.util.List;
import java.util.Map;

/**
 * Created by Peter.Li on 2015/10/9.
 */
@Service
public class ProjectServiceImpl implements ProjectService {
    private static Logger logger = LoggerFactory.getLogger(ProjectServiceImpl.class);

    @Autowired
    private ProjectDao projectDao;

    @Autowired
    private CPWarningRuleDao cpWarningRuleDao;

    @Autowired
    private ControlChartWarningRuleDao controlChartWarningRuleDao;

    @Autowired
    private PSAttributeDao psAttributeDao;

    @Autowired
    private StatisticAnalysisParameterDao statisticAnalysisParameterDao;


    @Autowired
    private DataDictionaryService dataDictionaryService;

    @Autowired
    private GroupService groupService;

    @Autowired
    private SecurityService securityService;

    @Autowired
    private PsGRRDao psGRRDao;

    protected JsonMapper mapper = JsonMapper.nonDefaultMapper();


    @Override
    public void saveProject(ProjectConfigureParameDto parameDto) {

        if (parameDto == null || StringUtils.isBlank(parameDto.getCode()) || StringUtils.isBlank(parameDto.getName())) {
            throw new ApplicationException(ExceptionMessages.EXCEPTION_PARAMETER_INPUT_INVALID);
        }

        Project project = projectDao.findByNameOrCode(parameDto.getName(), parameDto.getCode());
        if (project != null) {
            throw new ApplicationException(ExceptionMessages.EXCEPTION_OBJECT_ALREADY_EXISTS);
        }

        project = new Project();
        BeanMapper.copy(parameDto, project);

        logger.debug("Saving project:{}", parameDto);
        project.setCreateBy(securityService.getCurrentUserLoginName());
        projectDao.save(project);
        logger.info("Save done, project={}", project.getCode());

        StatisticAnalysisParameter statisticAnalysisParameterEntity = new StatisticAnalysisParameter();
        BeanMapper.copy(parameDto.getStatisticAnalysisParameterDto(), statisticAnalysisParameterEntity);
        statisticAnalysisParameterEntity.setProject(project);
        statisticAnalysisParameterDao.save(statisticAnalysisParameterEntity);
        logger.info("Save done, cpWarningRule={}", statisticAnalysisParameterEntity.getControlChartGroupingNumber());

        List<ControlChartWarningRuleDto> ccwrList = parameDto.getCcwrList();
        for (int i = 0; i < ccwrList.size(); i++) {
            ControlChartWarningRule controlChartWarningRuleEntity = new ControlChartWarningRule();
            BeanMapper.copy(ccwrList.get(i), controlChartWarningRuleEntity);
            controlChartWarningRuleEntity.setProject(project);
            controlChartWarningRuleDao.save(controlChartWarningRuleEntity);
        }

        List<CPWarningRuleDto> cpwrList = parameDto.getCpwrList();
        for (int i = 0; i < cpwrList.size(); i++) {
            CPWarningRule cpWarningRuleEntity = new CPWarningRule();
            BeanMapper.copy(cpwrList.get(i), cpWarningRuleEntity);
            cpWarningRuleEntity.setProject(project);
            cpWarningRuleDao.save(cpWarningRuleEntity);
        }

        List<PSAttributeDto> psaList = parameDto.getPsaList();
        for (int i = 0; i < psaList.size(); i++) {
            PSAttribute psAttributeEntity = new PSAttribute();
            BeanMapper.copy(psaList.get(i), psAttributeEntity);
            psAttributeEntity.setProject(project);
            psAttributeDao.save(psAttributeEntity);
        }

        parameDto.getPsgrrDto().setProjectId(project.getId());
        addPSGRR(parameDto.getPsgrrDto());
//        addPSGRR(new PSGRRDto());

    }

    @Override
    public void isCode(String code) {

        if (code == null || StringUtils.isBlank(code)) {
            throw new ApplicationException(ExceptionMessages.EXCEPTION_PARAMETER_INPUT_INVALID);
        }

        Project projectEntity = projectDao.findByCode(code);

        if (projectEntity != null) {
            throw new ApplicationException(ExceptionMessages.EXCEPTION_OBJECT_ALREADY_EXISTS);
        }
    }

    @Override
    public void isName(String name) {

        if (name == null || StringUtils.isBlank(name)) {
            throw new ApplicationException(ExceptionMessages.EXCEPTION_PARAMETER_INPUT_INVALID);
        }

        Project projectEntity = projectDao.findByName(name);
        if (projectEntity != null) {
            throw new ApplicationException(ExceptionMessages.EXCEPTION_OBJECT_ALREADY_EXISTS);
        }
    }

    @Override
    public List<StatisticAnalysisParameterDto> findStatisticAnalysisParameterByProjectId(String projectId) {
        if (projectId == null) {
            throw new ApplicationException(ExceptionMessages.EXCEPTION_PARAMETER_INPUT_INVALID);
        }

        Specification<StatisticAnalysisParameter> spec = new Specification<StatisticAnalysisParameter>() {
            public Predicate toPredicate(Root<StatisticAnalysisParameter> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                Join<StatisticAnalysisParameter, Project> leftJoin = root.join(root.getModel().getSingularAttribute("project", Project.class), JoinType.LEFT);
                Predicate predicate = cb.equal(leftJoin.get("id").as(String.class), projectId);
                query.where(predicate);
                return query.getRestriction();
            }
        };

        List<StatisticAnalysisParameter> list = statisticAnalysisParameterDao.findAll(spec);

        List<StatisticAnalysisParameterDto> dtos = Lists.newArrayList();
        for (StatisticAnalysisParameter entity : list) {
            StatisticAnalysisParameterDto dto = new StatisticAnalysisParameterDto();
            BeanMapper.copy(entity, dto);
            dtos.add(dto);
        }

        return dtos;
    }

    @Override
    public void updateStatisticAnalysisParameter(StatisticAnalysisParameterDto statisticAnalysisParameterDto) {

        if (statisticAnalysisParameterDto == null || statisticAnalysisParameterDto.getId() == null) {
            throw new ApplicationException(ExceptionMessages.EXCEPTION_PARAMETER_INPUT_INVALID);
        }

        StatisticAnalysisParameter statisticAnalysisParameter = statisticAnalysisParameterDao.findOne(statisticAnalysisParameterDto.getId());
        BeanMapper.copy(statisticAnalysisParameterDto, statisticAnalysisParameter);

        statisticAnalysisParameterDao.save(statisticAnalysisParameter);
    }

    @Override
    public void updateCPWarningRule(CPWarningRuleDto cpWarningRuleDto) throws Exception {
        if (cpWarningRuleDto == null || cpWarningRuleDto.getId() == null) {
            throw new ApplicationException(ExceptionMessages.EXCEPTION_PARAMETER_INPUT_INVALID);
        }

        CPWarningRule cpWarningRule = cpWarningRuleDao.findOne(cpWarningRuleDto.getId());
        cpWarningRule = (CPWarningRule) BeanUtil.CopyBeanToBean(cpWarningRuleDto, cpWarningRule);
//        BeanMapper.copy(cpWarningRule,cpWarningRule);
        cpWarningRuleDao.save(cpWarningRule);
    }

    @Override
    public void updateControlChartWarningRule(ControlChartWarningRuleDto controlChartWarningRuleDto) throws Exception {

        if (controlChartWarningRuleDto == null || controlChartWarningRuleDto.getId() == null) {
            throw new ApplicationException(ExceptionMessages.EXCEPTION_PARAMETER_INPUT_INVALID);
        }

        ControlChartWarningRule controlChartWarningRule = controlChartWarningRuleDao.findOne(controlChartWarningRuleDto.getId());
        controlChartWarningRule = (ControlChartWarningRule) BeanUtil.CopyBeanToBean(controlChartWarningRuleDto, controlChartWarningRule);

        controlChartWarningRuleDao.save(controlChartWarningRule);
    }

    @Override
    public void updatePSAttribute(PSAttributeDto psAttributeDto) {
        if (psAttributeDto == null || psAttributeDto.getId() == null || psAttributeDto.getValue().trim().isEmpty() || psAttributeDto.getProject() == null) {
            throw new ApplicationException(ExceptionMessages.EXCEPTION_PARAMETER_INPUT_INVALID);
        }

        PSAttribute psAttribute = psAttributeDao.findOne(psAttributeDto.getId());

        List<PSAttribute> psAttributes = psAttributeDao.findByProjectId(psAttributeDto.getProject().getId());
        for (PSAttribute psa : psAttributes) {
            if (psa.getValue().equals(psAttributeDto.getValue()) && !psAttributeDto.getId().equals(psa.getId())) {
                throw new ApplicationException(ExceptionMessages.EXCEPTION_OBJECT_ALREADY_EXISTS);
            }
        }

        psAttribute.setValue(psAttributeDto.getValue());
        if (psAttributeDto.getIsShow() != null) {
            psAttribute.setIsShow(psAttributeDto.getIsShow());
        }

        if (psAttributeDto.getIsDefault() != null) {
            psAttribute.setIsDefault(psAttributeDto.getIsDefault());
        }

        psAttributeDao.save(psAttribute);
    }

    @Override
    public void updatePSGRR(PSGRRDto psGRRDto) {
        if (psGRRDto == null || psGRRDto.getId() == null) {
            throw new ApplicationException(ExceptionMessages.EXCEPTION_PARAMETER_INPUT_INVALID);
        }

        PSGRR psgrr = new PSGRR();
        BeanMapper.copy(psGRRDto, psgrr);
        psGRRDao.save(psgrr);
    }

    @Override
    public void addControlChartWarningRule(ControlChartWarningRuleDto controlChartWarningRuleDto) {

        if (controlChartWarningRuleDto == null) {
            throw new ApplicationException(ExceptionMessages.EXCEPTION_PARAMETER_INPUT_INVALID);
        }

        ControlChartWarningRule controlChartWarningRule = new ControlChartWarningRule();
        BeanMapper.copy(controlChartWarningRuleDto, controlChartWarningRule);
        controlChartWarningRuleDao.save(controlChartWarningRule);

    }

    @Override
    public void addCPWarningRule(CPWarningRuleDto cpWarningRuleDto) {

        if (cpWarningRuleDto == null) {
            throw new ApplicationException(ExceptionMessages.EXCEPTION_PARAMETER_INPUT_INVALID);
        }

        CPWarningRule cpWarningRule = new CPWarningRule();
        BeanMapper.copy(cpWarningRuleDto, cpWarningRule);
        cpWarningRuleDao.save(cpWarningRule);
    }

    @Override
    public void addPSAttribute(PSAttributeDto psAttributeDto) {

        if (psAttributeDto == null || psAttributeDto.getValue() == null) {
            throw new ApplicationException(ExceptionMessages.EXCEPTION_PARAMETER_INPUT_INVALID);
        }

        List<PSAttribute> psAttributes = psAttributeDao.findByValue(psAttributeDto.getValue());
        for (PSAttribute psa : psAttributes) {
            if (psa.getProject().getId().equals(psAttributeDto.getProject().getId())) {
                throw new ApplicationException(ExceptionMessages.EXCEPTION_OBJECT_ALREADY_EXISTS);
            }
        }

        List<PSAttribute> psAttributeN = psAttributeDao.findByName(psAttributeDto.getName());
        for (PSAttribute psan : psAttributeN) {
            if (psan.getProject().getId().equals(psAttributeDto.getProject().getId())) {
                throw new ApplicationException(ExceptionMessages.EXCEPTION_OBJECT_ALREADY_EXISTS);
            }
        }

        PSAttribute psAttribute = new PSAttribute();
        BeanMapper.copy(psAttributeDto, psAttribute);
        psAttributeDao.save(psAttribute);
    }

    @Override
    public void addPSGRR(PSGRRDto psGRRDto) {
        if (psGRRDto == null) {
            throw new ApplicationException(ExceptionMessages.EXCEPTION_PARAMETER_INPUT_INVALID);
        }

        PSGRR psgrr = new PSGRR();
//        psGRRDto.setId(1L);
//        psGRRDto.setProjectId(1L);
//        psGRRDto.setRule("[10,20,30]");
//        psGRRDto.setModel("Normal");
//        psGRRDto.setSigma("6");
//        psGRRDto.setDefaultModelValue("[10, 3, 4]");//Part, Operator, Trial
        BeanMapper.copy(psGRRDto, psgrr);
        psGRRDao.save(psgrr);
    }

    @Override
    public void deletePSAttribute(String psaid) {
        if (psaid == null) {
            throw new ApplicationException(ExceptionMessages.EXCEPTION_PARAMETER_INPUT_INVALID);
        }
        PSAttribute psAttribute = psAttributeDao.findOne(Long.valueOf(psaid));
        if (psAttribute == null) {
            throw new ApplicationException(ExceptionMessages.EXCEPTION_OBJECT_DOES_NOT_EXIST);
        }
        psAttributeDao.delete(psAttribute);

    }

    @Override
    public void updateProject(ProjectDto projectDto) {
        if (projectDto == null || projectDto.getId() == null || StringUtils.isBlank(projectDto.getCode())) {
            throw new ApplicationException(ExceptionMessages.EXCEPTION_PARAMETER_INPUT_INVALID);
        }

        Project entity = new Project();
        BeanMapper.copy(projectDto, entity);

        logger.debug("Updating project:{}", projectDto);
        projectDao.save(entity);
        logger.info("Update done, project={}", entity.getCode());
    }

    @Override
    public void deleteProject(String id) {

    }

    @Override
    public ProjectDto findById(String id) {
        if (id == null) {
            throw new ApplicationException(ExceptionMessages.EXCEPTION_PARAMETER_INPUT_INVALID);
        }

        Project entity = projectDao.findById(Long.valueOf(id));

        if (entity == null) {
            throw new ApplicationException(ExceptionMessages.EXCEPTION_OBJECT_DOES_NOT_EXIST);
        }

        ProjectDto projectDto = new ProjectDto();
        BeanMapper.copy(entity, projectDto);

        return projectDto;
    }

    @Override
    public ProjectDto findByCode(String code) {
        if (code == null) {
            throw new ApplicationException(ExceptionMessages.EXCEPTION_PARAMETER_INPUT_INVALID);
        }

        Project entity = projectDao.findByCode(code);

        if (entity == null) {
            throw new ApplicationException(ExceptionMessages.EXCEPTION_OBJECT_DOES_NOT_EXIST);
        }

        ProjectDto dto = new ProjectDto();
        BeanMapper.copy(entity, dto);

        return dto;
    }

    @Override
    public ProjectDto findByName(String name) {
        if (name == null) {
            throw new ApplicationException(ExceptionMessages.EXCEPTION_PARAMETER_INPUT_INVALID);
        }

        Project entity = projectDao.findByName(name);

        if (entity == null) {
            throw new ApplicationException(ExceptionMessages.EXCEPTION_OBJECT_DOES_NOT_EXIST);
        }

        ProjectDto dto = new ProjectDto();
        BeanMapper.copy(entity, dto);

        return dto;
    }

    @Override
    public List<ProjectDto> findAllProjects() {
        return null;
    }

    @Override
    public List<ProjectDto> searchProject(Map<String, Object> searchParams) {
        return null;
    }

    @Override
    public Page<ProjectDto> searchProject(Map<String, Object> searchParams, Pageable pageable) {
        logger.debug("Searching project with {},{},{}", searchParams.keySet(), searchParams.values(), pageable);
        CredentialsUserDto userDto = securityService.getCurrentUser();
        if (userDto == null) {
            throw new ApplicationException(ExceptionMessages.EXCEPTION_PARAMETER_INPUT_INVALID);
        }
        List<ProjectDto> projectDtos = Lists.newArrayList();
        Map<String, SearchFilter> filters = SearchFilter.parse(searchParams);

        String userId = userDto.getId().toString();
        String name = userDto.getLoginName();

        List<GroupDto> groupDtoList = groupService.findGroupsForUser(userId);
        boolean isAdministrator = false;
        if (!groupDtoList.isEmpty() && groupDtoList.get(0).isAdministrator()) {
            isAdministrator = true;
        }

        Page<Project> projectPage = new PageImpl(projectDtos);
        if (isAdministrator) {
            Specification<Project> spec = DynamicSpecifications.bySearchFilter(filters.values(), Project.class);
            projectPage = projectDao.findAll(spec, pageable);
        } else {
            Specification<Project> spec = new Specification<Project>() {
                public Predicate toPredicate(Root<Project> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                    ListJoin groupListJoin = root.joinList("groups", JoinType.LEFT);
                    List<Predicate> orPredicates = Lists.newArrayList();
                    if (groupDtoList != null && !groupDtoList.isEmpty()) {
                        for (GroupDto groupDto : groupDtoList) {
                            CriteriaBuilder.In<Long> in = cb.in(groupListJoin.get("id").as(Long.class));
                            in.value(groupDto.getId());
                            orPredicates.add(cb.or(in));
                        }
                    }

                    if (StringUtils.isNotBlank(name)) {
                        orPredicates.add(cb.or(cb.equal(root.get("createBy"), name)));
                    }

                    Predicate p = cb.or(orPredicates.toArray(new Predicate[orPredicates.size()]));
                    Predicate o = DynamicSpecifications.bySearchFilter(filters.values(), Project.class).toPredicate(root, query, cb);

                    query.distinct(true);
                    query.where(p, o);

                    return query.getRestriction();
                }
            };
            projectPage = projectDao.findAll(spec, pageable);
        }

        logger.debug("Searching project, total elements:{}", projectPage.getTotalElements());

        List<Project> projects = projectPage.getContent();
        for (Project project : projects) {
            ProjectDto projectDto = new ProjectDto();
            BeanMapper.copy(project, projectDto);
            projectDtos.add(projectDto);
        }

        Page<ProjectDto> pageDto = new PageImpl<ProjectDto>(projectDtos, pageable, projectPage.getTotalElements());

        return pageDto;
    }


    @Override
    public Page<CPWarningRuleDto> findCpWarningRulesByProject(String projectId, Pageable pageable) {
        if (projectId == null) {
            throw new ApplicationException(ExceptionMessages.EXCEPTION_PARAMETER_INPUT_INVALID);
        }

        Specification<CPWarningRule> spec = new Specification<CPWarningRule>() {
            public Predicate toPredicate(Root<CPWarningRule> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                Join<CPWarningRule, Project> leftJoin = root.join(root.getModel().getSingularAttribute("project", Project.class), JoinType.LEFT);
                Predicate predicate = cb.equal(leftJoin.get("id").as(String.class), projectId);
                query.where(predicate);
                return query.getRestriction();
            }
        };

        Page<CPWarningRule> cpwrPage = cpWarningRuleDao.findAll(spec, pageable);
        List<CPWarningRule> list = cpwrPage.getContent();

        List<CPWarningRuleDto> dtos = Lists.newArrayList();
        for (CPWarningRule entity : list) {
            CPWarningRuleDto dto = new CPWarningRuleDto();
            BeanMapper.copy(entity, dto);
            dtos.add(dto);
        }
        Page<CPWarningRuleDto> pageDto = new PageImpl<CPWarningRuleDto>(dtos, pageable, cpwrPage.getTotalElements());
        return pageDto;
    }

    @Override
    public Page<ControlChartWarningRuleDto> findControlChartWarningRuleByProject(String projectId, Pageable pageable) {

        if (projectId == null) {
            throw new ApplicationException(ExceptionMessages.EXCEPTION_PARAMETER_INPUT_INVALID);
        }

        Specification<ControlChartWarningRule> spec = new Specification<ControlChartWarningRule>() {
            public Predicate toPredicate(Root<ControlChartWarningRule> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                Join<ControlChartWarningRule, Project> leftJoin = root.join(root.getModel().getSingularAttribute("project", Project.class), JoinType.LEFT);
                Predicate predicate = cb.equal(leftJoin.get("id").as(String.class), projectId);
                query.where(predicate);
                return query.getRestriction();
            }
        };

        Page<ControlChartWarningRule> ccwrPage = controlChartWarningRuleDao.findAll(spec, pageable);
        List<ControlChartWarningRule> list = ccwrPage.getContent();

        List<ControlChartWarningRuleDto> dtos = Lists.newArrayList();
        for (ControlChartWarningRule entity : list) {
            ControlChartWarningRuleDto dto = new ControlChartWarningRuleDto();
            BeanMapper.copy(entity, dto);
            dtos.add(dto);
        }
        Page<ControlChartWarningRuleDto> pageDto = new PageImpl<ControlChartWarningRuleDto>(dtos, pageable, ccwrPage.getTotalElements());
        return pageDto;
    }

    @Override
    public Page<PSAttributeDto> findPSAttributeByProject(String projectId, Pageable pageable) {

        if (projectId == null) {
            throw new ApplicationException(ExceptionMessages.EXCEPTION_PARAMETER_INPUT_INVALID);
        }

        Specification<PSAttribute> spec = new Specification<PSAttribute>() {
            public Predicate toPredicate(Root<PSAttribute> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                Join<PSAttribute, Project> leftJoin = root.join(root.getModel().getSingularAttribute("project", Project.class), JoinType.LEFT);
                Predicate predicate = cb.equal(leftJoin.get("id").as(String.class), projectId);
                query.where(predicate);
                query.orderBy(cb.desc(root.get("isDefault")));

                return query.getRestriction();
            }
        };
        Page<PSAttribute> psaPage = psAttributeDao.findAll(spec, pageable);
        List<PSAttribute> list = psaPage.getContent();

        List<PSAttributeDto> dtos = Lists.newArrayList();
        List<PSAttributeDto> defaultDtos = Lists.newArrayList();
        for (PSAttribute entity : list) {
            PSAttributeDto dto = new PSAttributeDto();
            BeanMapper.copy(entity, dto);
            if(dto.getIsDefault()){
                defaultDtos.add(dto);
            } else {
                dtos.add(dto);
            }
        }
        Collections.sort(dtos);
        List<PSAttributeDto> resultDtos = Lists.newArrayList();
        resultDtos.addAll(defaultDtos);
        resultDtos.addAll(dtos);

        Page<PSAttributeDto> pageDto = new PageImpl<PSAttributeDto>(resultDtos, pageable, psaPage.getTotalElements());
        return pageDto;
    }

    @Override
    public PSGRRDto findPSGRRByProject(String projectId) {
        if (projectId == null) {
            throw new ApplicationException(ExceptionMessages.EXCEPTION_PARAMETER_INPUT_INVALID);
        }

        PSGRR psgrr = psGRRDao.findByProjectId(Long.valueOf(projectId));
        if (psgrr == null) {
            throw new ApplicationException(ExceptionMessages.EXCEPTION_OBJECT_DOES_NOT_EXIST);
        }
        PSGRRDto psgrrDto = new PSGRRDto();
        BeanMapper.copy(psgrr, psgrrDto);

        return psgrrDto;
    }

    @Override
    public List<PSAttributeDto> findPSAttributeByProject(String projectId) {

        if (projectId == null) {
            throw new ApplicationException(ExceptionMessages.EXCEPTION_PARAMETER_INPUT_INVALID);
        }

        Specification<PSAttribute> spec = new Specification<PSAttribute>() {
            public Predicate toPredicate(Root<PSAttribute> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                Join<PSAttribute, Project> leftJoin = root.join(root.getModel().getSingularAttribute("project", Project.class), JoinType.LEFT);
                Predicate predicate = cb.equal(leftJoin.get("id").as(String.class), projectId);
                query.where(predicate);
                query.orderBy(cb.desc(root.get("isDefault")));

                return query.getRestriction();
            }
        };
        List<PSAttribute> list = psAttributeDao.findAll(spec);

        List<PSAttributeDto> dtos = Lists.newArrayList();
        List<PSAttributeDto> defaultDtos = Lists.newArrayList();
        for (PSAttribute entity : list) {
            PSAttributeDto dto = new PSAttributeDto();
            BeanMapper.copy(entity, dto);
            if(dto.getIsDefault()){
                defaultDtos.add(dto);
            } else {
                dtos.add(dto);
            }
        }
        Collections.sort(dtos);
        List<PSAttributeDto> resultDtos = Lists.newArrayList();
        resultDtos.addAll(defaultDtos);
        resultDtos.addAll(dtos);
        return resultDtos;
    }

    @Override
    public List<ControlChartWarningRuleDto> findDefaultControlChartWarningRuleDto() {

        List<DataDictionaryDto> dataDictionaryDtos = dataDictionaryService.findByCategoryAndParent(DDCategory.CCWR, null);
        List<ControlChartWarningRuleDto> ruleList = Lists.newArrayList();
        for (DataDictionaryDto dto : dataDictionaryDtos) {
            ControlChartWarningRuleDto rule = new ControlChartWarningRuleDto();
            DataDictionaryReflectUtil.copyAttribute2Object(dto.getSubDataDictionaries(), rule);
            rule.setId(dto.getId());
            ruleList.add(rule);
        }
        return ruleList;
    }

    @Override
    public List<CPWarningRuleDto> findDefaultCPWarningRuleDto() {
        List<DataDictionaryDto> dataDictionaryDtos = dataDictionaryService.findByCategoryAndParent(DDCategory.CPWR, null);
        List<CPWarningRuleDto> ruleList = Lists.newArrayList();
        for (DataDictionaryDto dto : dataDictionaryDtos) {
            CPWarningRuleDto rule = new CPWarningRuleDto();
            DataDictionaryReflectUtil.copyAttribute2Object(dto.getSubDataDictionaries(), rule);
            rule.setId(dto.getId());
            ruleList.add(rule);
        }
        return ruleList;
    }

    @Override
    public List<PSAttributeDto> findDefaultPSAttributeDto() {
        List<DataDictionaryDto> dataDictionaryDtos = dataDictionaryService.findByCategoryAndParent(DDCategory.PSA, null);
        List<PSAttributeDto> attributeList = Lists.newArrayList();
        for (DataDictionaryDto dto : dataDictionaryDtos) {
            for (DataDictionaryDto dto2 : dto.getSubDataDictionaries()) {
                PSAttributeDto attribute = new PSAttributeDto();
                attribute.setName(dto2.getAttributeName());
                attribute.setValue(dto2.getAttributeValue());
                attribute.setIsDefault(true);
                attribute.setId(dto2.getId());
                attributeList.add(attribute);
            }

        }
        return attributeList;
    }

    @Override
    public void checkNameOrCode(Long id, String name, String code) {
        boolean isExist = false;
        Project project = projectDao.findById(id);
        if (project == null) {
            throw new ApplicationException(ExceptionMessages.EXCEPTION_OBJECT_DOES_NOT_EXIST);
        }
        Project project1 = projectDao.findByName(name);
        if (project1 != null) {
            isExist = true;
            if (name != null && project.getName().equals(name)) {
                isExist = false;
            }
        }

        Project project2 = projectDao.findByCode(code);
        if (project2 != null) {
            isExist = true;
            if (code != null && project.getCode().equals(code)) {
                isExist = false;
            }
        }
        if (isExist) {
            throw new ApplicationException(ExceptionMessages.EXCEPTION_OBJECT_ALREADY_EXISTS);
        }
    }
}
